

#import "DownloadManager.h"

@interface DownloadManager () <NSURLSessionDownloadDelegate>

@end

@implementation DownloadManager {
    
    /// 全局下载的session
    NSURLSession *_downloadSession;
    /// 进度回调缓存池
    NSMutableDictionary *_progressBlockDict;
    /// 完成回调缓存池
    NSMutableDictionary *_completionBlockDict;
    /// 下载任务缓存池
    NSMutableDictionary *_downlaodTaskDict;
}

+ (instancetype)sharedManager
{
    static id instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    
    return instance;
}

- (instancetype)init
{
    if (self = [super init]) {
        
        // 实例化全局下载session
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"HM"];
        _downloadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
        
        // 实例化进度回调缓存池 / 完成回调缓存池 / 下载任务缓存池
        _progressBlockDict = [[NSMutableDictionary alloc] init];
        _completionBlockDict = [[NSMutableDictionary alloc] init];
        _downlaodTaskDict = [[NSMutableDictionary alloc] init];
    }
    
    return self;
}

/// 单例下载的主方法
- (void)downloadWithURLString:(NSString *)URLString progress:(void (^)(float))progressBlock completion:(void (^)(NSString *))completionBlock
{
    // 1. URL
    NSURL *URL = [NSURL URLWithString:URLString];
    
    // 获取续传数据
    NSData *resumeData = [NSData dataWithContentsOfFile:[URLString appendTempPath]];
    
    // 3. 自定义session发起和启动任务
    NSURLSessionDownloadTask *downloadTask;
    
    // 3.1 如果有续传数据就继续下载
    if (resumeData != nil) {
        
        downloadTask = [_downloadSession downloadTaskWithResumeData:resumeData];
        
        // 注意 : 续传数据使用完一定要及时的移除,再次暂停时会重新的生成续传数据!!!
        [[NSFileManager defaultManager] removeItemAtPath:[URLString appendTempPath] error:NULL];
    } else {
        // 3.2 如果没有续传数据就重新下载
        downloadTask = [_downloadSession downloadTaskWithURL:URL];
    }
    
    // 4. 把VC传入的进度回调 / 完成回调 保存到缓存池
    // 提示 : 以downloadTask作为key的目的是,需要在代理里面取这个回调,但是代理方法只有downloadTask
    [_progressBlockDict setObject:progressBlock forKey:downloadTask];
    [_completionBlockDict setObject:completionBlock forKey:downloadTask];
    
    // 5.把下载任务添加到缓存池
    [_downlaodTaskDict setObject:downloadTask forKey:URLString];
    
    // 6. 启动任务
    [downloadTask resume];
}

/// 判断是否正在下载
- (BOOL)checkIsDownloadingWithURLString:(NSString *)URLString
{
    if ([_downlaodTaskDict objectForKey:URLString] != nil) {
        return YES;
    }
    
    return NO;
}

/// 暂停下载的主方法
- (void)pauseDownloadWithURLString:(NSString *)URLString pauseBlock:(void (^)())pauseBlock
{
    // 1.取出正在下载的任务
    NSURLSessionDownloadTask *downloadTask = [_downlaodTaskDict objectForKey:URLString];
    
    // 2.暂停这个正在下载的任务
    if (downloadTask) {
        
        [downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
            // 3.把续传数据缓存到沙盒
            [resumeData writeToFile:[URLString appendTempPath] atomically:YES];
            
            // 4.清空相关缓存池
            [_progressBlockDict removeObjectForKey:downloadTask];
            [_completionBlockDict removeObjectForKey:downloadTask];
            [_downlaodTaskDict removeObjectForKey:URLString];
            
            // 5.把暂停的结果回到到VC
            if (pauseBlock) {
                pauseBlock();
            }
        }];
    }
}

#pragma NSURLSessionDownloadDelegate
/*
 接下来要做的事情 : 把对应的cell上的下载进度回调到控制器
 存在的问题 : 如何区别哪个进度的回调是对应哪个cell的?
 提示 : 不能用属性记录外界传入的progressBlock,后面的会覆盖前面的
 解决办法 : 使用字典实现代码块缓存池;
 */

/// 监听文件下载进度
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    // 1. 计算进度
    float progress = (float)totalBytesWritten / totalBytesExpectedToWrite;

    // 2. 从缓存池去进度回调
    void (^progressBlock)(float) = [_progressBlockDict objectForKey:downloadTask];
    
    // 3. 把下载进度回调到控制器
    if (progressBlock) {
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            progressBlock(progress);
        }];
    }
}

/// 监听文件下载完成
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
//    NSLog(@"DownloadManager 文件下载完成 %@",location.path);
    
    // 1.把下载完成的音频文件缓存到沙盒
    NSString *URLString = downloadTask.currentRequest.URL.absoluteString;
    NSString *fileName = [URLString lastPathComponent];
    NSString *savePath = [NSString stringWithFormat:@"/Users/xiele/Desktop/%@",fileName];
    [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:savePath error:NULL];
    
    // 2.取出下载完成回调
    void(^completionBlock)(NSString *) = [_completionBlockDict objectForKey:downloadTask];
    
    // 3.回调路径到控制器
    if (completionBlock) {
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            completionBlock(savePath);
        }];
    }
    
    // 4.下载完成之后把相关缓存池清空
    [_progressBlockDict removeObjectForKey:downloadTask];
    [_completionBlockDict removeObjectForKey:downloadTask];
    [_downlaodTaskDict removeObjectForKey:URLString];
}

@end
